package com.nulldev.util.data;

import com.nulldev.util.internal.codedapter.Nullable;
import com.nulldev.util.logging.Logger.Level;
import com.nulldev.util.logging.LoggerFactory;

public class Variables {
	public static boolean notNull(final Object object) {
		return (object != null);
	}

	public static <T> void requireNonNull(final T object, final String name) {
		if (object == null)
			throw new NullPointerException(name + " == null!");
	}

	public static <T> T requireNonNullObject(final T object, final String name) {
		if (object == null) {
			throw new NullPointerException(name + " == null!");
		} else {
			return object;
		}
	}

	public static void notNullE(final Object value) {
		if (value == null) {
			throw new NullPointerException("value == null!");
		}
	}

	public static boolean isNumber(final Object loc) {
		return loc instanceof Number;
	}

	/* START GUAVA CODE */
	/*
	 * Copyright (C) 2010 The Guava Authors
	 *
	 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
	 * use this file except in compliance with the License. You may obtain a copy of
	 * the License at
	 *
	 * http://www.apache.org/licenses/LICENSE-2.0
	 *
	 * Unless required by applicable law or agreed to in writing, software
	 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
	 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
	 * License for the specific language governing permissions and limitations under
	 * the License.
	 */

	public static int checkPositionIndex(int index, int size) {
		return checkPositionIndex(index, size, "index");
	}

	public static int checkPositionIndex(int index, int size, @Nullable String desc) {
		// Carefully optimized for execution by hotspot (explanatory comment above)
		if (index < 0 || index > size) {
			throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc));
		}
		return index;
	}

	private static String badPositionIndex(int index, int size, @Nullable String desc) {
		if (index < 0) {
			return lenientFormat("%s (%s) must not be negative", desc, index);
		} else if (size < 0) {
			throw new IllegalArgumentException("negative size: " + size);
		} else { // index > size
			return lenientFormat("%s (%s) must not be greater than size (%s)", desc, index, size);
		}
	}

	public static <T> T checkNotNull(final T value) {
		if (value == null) {
			throw new NullPointerException();
		} else
			return value;
	}

	public static <T extends Object> T checkNotNull(T reference, @Nullable Object errorMessage) {
		if (reference == null) {
			throw new NullPointerException(String.valueOf(errorMessage));
		}
		return reference;
	}

	public static void checkArgument(boolean expression) {
		if (!expression) {
			throw new IllegalArgumentException();
		}
	}

	public static void checkArgument(boolean b, @Nullable String errorMessageTemplate, int p1) {
		if (!b) {
			throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1));
		}
	}

	public static void checkArgument(boolean expression, @Nullable String errorMessageTemplate, @Nullable Object... errorMessageArgs) {
		if (!expression) {
			throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, errorMessageArgs));
		}
	}

	public static void checkArgument(boolean b, @Nullable String errorMessageTemplate, @Nullable Object p1, @Nullable Object p2) {
		if (!b) {
			throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2));
		}
	}

	private static String lenientToString(@Nullable Object o) {
		if (o == null) {
			return "null";
		}
		try {
			return o.toString();
		} catch (Exception e) {
			// Default toString() behavior - see Object.toString()
			String objectToString = o.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(o));
			// Logger is created inline with fixed name to avoid forcing Proguard to create
			// another class.
			LoggerFactory.getLogger(Variables.class).log(Level.WARNING, "Exception during lenientFormat for " + objectToString, e);
			return "<" + objectToString + " threw " + e.getClass().getName() + ">";
		}
	}

	public static String lenientFormat(@Nullable String template, @Nullable Object... args) {
		template = String.valueOf(template); // null -> "null"

		if (args == null) {
			args = new Object[]
				{ "(Object[])null" };
		} else {
			for (int i = 0; i < args.length; i++) {
				args[i] = lenientToString(args[i]);
			}
		}

		// start substituting the arguments into the '%s' placeholders
		StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
		int templateStart = 0;
		int i = 0;
		while (i < args.length) {
			int placeholderStart = template.indexOf("%s", templateStart);
			if (placeholderStart == -1) {
				break;
			}
			builder.append(template, templateStart, placeholderStart);
			builder.append(args[i++]);
			templateStart = placeholderStart + 2;
		}
		builder.append(template, templateStart, template.length());

		// if we run out of placeholders, append the extra args in square braces
		if (i < args.length) {
			builder.append(" [");
			builder.append(args[i++]);
			while (i < args.length) {
				builder.append(", ");
				builder.append(args[i++]);
			}
			builder.append(']');
		}

		return builder.toString();
	}

	public static void checkState(boolean expression) {
		if (!expression) {
			throw new IllegalStateException();
		}
	}

	public static void checkState(boolean expression, @Nullable Object errorMessage) {
		if (!expression) {
			throw new IllegalStateException(String.valueOf(errorMessage));
		}
	}

	public static float checkArgumentFinite(final float value, final String valueName) {
		if (Float.isNaN(value)) {
			throw new IllegalArgumentException(valueName + " must not be NaN");
		} else if (Float.isInfinite(value)) {
			throw new IllegalArgumentException(valueName + " must not be infinite");
		}

		return value;
	}

	public static void checkArgumentNonnegative(int size) {
		if (size < 0)
			throw new IllegalArgumentException("Invalid value: " + size);
	}

	/**
	 * Precondition tester for {@code Iterator.remove()} that throws an exception
	 * with a consistent error message.
	 */
	public static void checkRemove(boolean canRemove) {
		checkState(canRemove, "no calls to next() since the last call to remove()");
	}

	/* END GUAVA CODE */
}
